package de.op.projectc.objects;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureWrap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Vector2;
import de.op.projectc.helper.CameraHelper;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;

public class TerrainWorld_old {

	private Texture texture;
	private ShaderProgram shader;
    private OrthographicCamera  camera;
	
	private final int SCROLLING_SPEED = 100; //Pixel
	private final float DIFFICULTY = 3f;
	private final int VERTIEX_DISTANCE = 20;
	private float totalScrolled = 0;
	
	private final Heli heli;
	//http://gamedev.stackexchange.com/questions/43658/dynamic-terrain-texture
	
	private Deque<Terrain> activeLeftTerrains;
	private Deque<Terrain> activeRightTerrains;
	
	//TODO: Fehler/Exceptions abfangen
	public TerrainWorld_old(Heli heli) {
		this.heli = heli;
		
		camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		camera.position.set(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, 0);
		
		texture = new Texture(Gdx.files.internal("dirt.jpg"));
		texture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
		
		activeLeftTerrains = new ArrayDeque<Terrain>();
		activeRightTerrains = new ArrayDeque<Terrain>();

		createShader();

		generateStartTerrain();
	}
	
	private void createShader() {
		String vertexShader = "attribute vec4 a_position;    \n" 
				+ "attribute vec4 a_color;       \n"
				+ "attribute vec2 a_texCoords;   \n"
				+ "uniform mat4 u_worldView;\n" 
				+ "varying vec4 v_color;         \n"
				+ "varying vec2 v_texCoords;     \n"
				+ "\n"
				+ "void main()                   \n"
				+ "{                             \n"
				+ "   v_color = vec4(1,1,1,1);//a_color;         \n"
				+ "   v_texCoords = a_texCoords; \n"
				+ "   gl_Position = u_worldView * a_position;  \n"
				+ "}                             \n";

		// this one tells it what goes in between the points (i.e colour/texture)
		String fragmentShader = "#ifdef GL_ES                \n" 
				+ "precision mediump float;    \n"
				+ "#endif                      \n" 
				+ "varying vec4 v_color;       \n"
				+ "varying vec2 v_texCoords;   \n"
				+ "uniform sampler2D u_texture;\n"
				+ "void main()                 \n"
				+ "{                           \n" 
				+ "  gl_FragColor = v_color * texture2D(u_texture, v_texCoords);   \n" 
				+ "}                           \n";	

		//http://meatcat.info/wordpress/?p=35
		shader = new ShaderProgram(vertexShader, fragmentShader);
		if (shader.isCompiled() == false) {
			Gdx.app.log("ShaderTest", shader.getLog());
			Gdx.app.exit();
		}
	}
	
	void generateStartTerrain() {
		int width = Gdx.graphics.getWidth();
		int mid = width / 2;
		int height = Gdx.graphics.getHeight();
		int startPosition = height;
		
		float freeSpace = this.heli.getWidth() * 1;
		
		ArrayList<Vector2> leftVertices = new ArrayList<Vector2>();
		ArrayList<Vector2> rightVertices = new ArrayList<Vector2>();
		leftVertices.add(new Vector2(0, startPosition));
		rightVertices.add(new Vector2(width, startPosition));
		
         for(int i = 0 ; i < height * DIFFICULTY; i += VERTIEX_DISTANCE) {
             
        	 //double position = Math.sin((2*Math.PI * ((float) i/height)) / DIFFICULTY) * mid;
        	 //double position = Math.sin((float) i * DIFFICULTY) * mid;
        	 double s = 2 * Math.PI * ((float) i/(height * DIFFICULTY));
        	 double position = Math.sin(s) * mid;
        	 
        	 position += mid;

            // Gdx.app.log("skyline[i]:", i + " - " + position);
             
        	 leftVertices.add(new Vector2((float) position - freeSpace, (float) startPosition + i));
        	 leftVertices.add(new Vector2(0, (float) startPosition + i));
        	 
        	 rightVertices.add(new Vector2((float) position + freeSpace, (float) startPosition + i));
        	 rightVertices.add(new Vector2(width, (float) startPosition + i));
         }
         leftVertices.add(new Vector2(0, startPosition + height * DIFFICULTY));
         rightVertices.add(new Vector2(width, startPosition + height * DIFFICULTY));
         
         this.activeLeftTerrains.add(new Terrain(leftVertices));
         this.activeRightTerrains.add(new Terrain(rightVertices));
	}
	
	void generateNextTerrain() {
		int width = Gdx.graphics.getWidth();
		int mid = width / 2;
		int height = Gdx.graphics.getHeight();

		float freeSpace = this.heli.getWidth() * 1;
		
		ArrayList<Vector2> leftVertices = new ArrayList<Vector2>();
		ArrayList<Vector2> rightVertices = new ArrayList<Vector2>();
		
		//kleine Ueberlappung, damit kein Flimmern auftaucht
		leftVertices.addAll(this.activeLeftTerrains.getLast().getLastVertices(4));
		rightVertices.addAll(this.activeRightTerrains.getLast().getLastVertices(4));

		int startPosition = (int) this.activeLeftTerrains.getLast().getLastVertex().y;
		
        for(int i = 0; i < height * DIFFICULTY; i += VERTIEX_DISTANCE) {
        	
        	double s = 2 * Math.PI * ((float) i/(height * DIFFICULTY));
        	double position = Math.sin(s) * mid;
        	
       	 	position += mid;
            
            //Gdx.app.log("next[i]:", 2 * Math.PI * ((float) i/height) + " - " + Math.sin(s * DIFFICULTY) * mid);
            
       	 	leftVertices.add(new Vector2((float) position - freeSpace, (float) startPosition + i));
       	 	leftVertices.add(new Vector2(0, (float) startPosition + i));

       	 	rightVertices.add(new Vector2((float) position + freeSpace, (float) startPosition + i));
       	 	rightVertices.add(new Vector2(width, (float) startPosition + i));
        }
        leftVertices.add(new Vector2(0, startPosition + height * DIFFICULTY));
        rightVertices.add(new Vector2(width, startPosition + height * DIFFICULTY));

        this.activeLeftTerrains.addLast(new Terrain(leftVertices));
        this.activeRightTerrains.addLast(new Terrain(rightVertices));
	}

	private void scrollTerrain(float scroll) {

		for(Terrain terrain : activeLeftTerrains) {
			terrain.move(0, -scroll);
		}
		
		for(Terrain terrain : activeRightTerrains) {
			terrain.move(0, -scroll);
		}

		this.totalScrolled += scroll;
		if(this.totalScrolled > Gdx.graphics.getHeight() - SCROLLING_SPEED) {
			generateNextTerrain();
			
			this.totalScrolled = -SCROLLING_SPEED;
		}
	}

	//http://gamedev.stackexchange.com/questions/57033/problem-implementing-texture-on-libgdx-mesh-of-randomized-terrain
	//http://stackoverflow.com/questions/17332352/how-to-crop-a-non-rectangular-section-out-of-an-image-in-libgdx
	public void update(float delta) {
		//cleanUp(delta * SCROLLING_SPEED);
		scrollTerrain(delta * SCROLLING_SPEED);
		
		//camera scrolling
	    //camera.translate(0, delta * SCROLLING_SPEED);		
		//camera.update();
	}
	
	//TODO: in Thread auslagern vllt -> Performance Check
	private void cleanUp(float scroll) {
		if(this.activeLeftTerrains.getFirst().getLastVertex().y - scroll <= 0) {
			this.activeLeftTerrains.removeFirst().dispose();
			this.activeRightTerrains.removeFirst().dispose();
		}
	}

	public void render(SpriteBatch spriteBatch) {
			GL20 gl = Gdx.graphics.getGL20();
			//gl.glClearColor(1f, 1f, 1f, 1);
			gl.glViewport(0, 0, (int)CameraHelper.getInstance().viewportWidth, (int)CameraHelper.getInstance().viewportHeight);
	
			//we don't necessarily need these, but its good practice to enable
			//the things we need. we enable 2d textures and set the active one
			//to 0. we could have multiple textures but we don't need it here.
			gl.glEnable(GL20.GL_TEXTURE_2D);
			gl.glActiveTexture(GL20.GL_TEXTURE0);
	
			//KP Testen
			//Gdx.gl20.glEnable(GL20.GL_BLEND);
			//Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
			
			shader.begin();
			//this sets our uniform 'u_texture' (i.e. the gl texture we want to use) to 0.
			shader.setUniformMatrix("u_worldView", CameraHelper.getInstance().combined);
			shader.setUniformi("u_texture", 0);
			texture.bind();

			//mesh.render(shader, GL20.GL_TRIANGLES);
			//testMesh.render(shader, GL20.GL_TRIANGLE_FAN);


			for(Terrain terrain : activeLeftTerrains) {
				//terrain.getTerrainMesh().render(shader, GL20.GL_TRIANGLES);
				
				//terrain.getTerrainMesh().render(shader, GL20.GL_TRIANGLE_STRIP);
				
				//terrain.getTerrainMesh().render(shader, GL20.GL_TRIANGLE_FAN);
				//terrain.getTerrainMesh().render(shader, GL20.GL_POINTS);
				terrain.getTerrainMesh().render(shader, GL20.GL_LINES);
			}
			
			for(Terrain terrain : activeRightTerrains) {
				//terrain.getTerrainMesh().render(shader, GL20.GL_TRIANGLES);
				
				terrain.getTerrainMesh().render(shader, GL20.GL_TRIANGLE_STRIP);
				
				//terrain.getTerrainMesh().render(shader, GL20.GL_TRIANGLE_FAN);
				//terrain.getTerrainMesh().render(shader, GL20.GL_POINTS);
				terrain.getTerrainMesh().render(shader, GL20.GL_LINES);
			}

	/*
			if(waitingForSprite && this.sprite == null) {
				//sprite = new GameObject(new Sprite(ScreenUtils.getFrameBufferTexture(0, (int) this.activeLeftTerrains.getLast().getLastVertex().y, Gdx.graphics.getWidth(), Gdx.graphics.getHeight())),
				//			0f, this.activeLeftTerrains.getLast().getLastVertex().y);
				sprite = new Sprite(ScreenUtils.getFrameBufferTexture(0, Gdx.graphics.getHeight()/2, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()));
				sprite.setColor(Color.BLUE);
				
				Gdx.app.log("sprite", ": " + sprite.getX() + " - " + sprite.getY());
				waitingForSprite = false;
			}
			
			if(sprite != null) {
				spriteBatch.begin();
				sprite.draw(spriteBatch);
		        spriteBatch.end();
			}
			*/
			shader.end();
	}

	public void dispose() {
		this.texture.dispose();
		this.shader.dispose();
		
		for(Terrain terrain : activeLeftTerrains) {
			terrain.dispose();
		}
		for(Terrain terrain : activeRightTerrains) {
			terrain.dispose();
		}
	}
}
